using NetPad.Apps.Data.EntityFrameworkCore.DataConnections;
using NetPad.Common;
using NetPad.Configuration;
using NetPad.Data;
using NetPad.Data.Metadata;
using NetPad.Sessions;

namespace NetPad.Apps.Data.EntityFrameworkCore;

internal class EntityFrameworkDatabaseConnectionMetadataProvider(
    IDataConnectionResourcesCache dataConnectionResourcesCache,
    ISession session)
    : IDatabaseConnectionMetadataProvider
{
    public async Task<DatabaseStructure> GetDatabaseStructureAsync(DatabaseConnection databaseConnection)
    {
        if (databaseConnection is not EntityFrameworkDatabaseConnection dbConnection)
        {
            throw new ArgumentException("Cannot get structure except on Entity Framework database connections",
                nameof(databaseConnection));
        }

        // If we have something scaffolded already, use that.
        var cached = await GetCachedDatabaseStructureAsync(dbConnection);
        if (cached != null)
        {
            return cached;
        }

        // If we are going to scaffold to get structure, prioritize the active script's framework version.
        // This will generate resources for the user's currently opened script which the user will likely run after
        // they've inspected the structure.
        var frameworkVersion = session.Active != null
            ? session.Active.Script.Config.TargetFrameworkVersion
            : GlobalConsts.AppDotNetFrameworkVersion;

        var resources = await dataConnectionResourcesCache.GetResourcesAsync(dbConnection, frameworkVersion);

        return resources.DatabaseStructure ??
               throw new Exception(
                   $"Could not get database structure, no structure was successfully scaffolded. " +
                   $"Check the logs at: {AppDataProvider.LogDirectoryPath}");
    }

    private async Task<DatabaseStructure?> GetCachedDatabaseStructureAsync(DatabaseConnection dbConnection)
    {
        var cachedVersions = await dataConnectionResourcesCache.GetCachedDotNetFrameworkVersions(dbConnection.Id);

        if (cachedVersions.Count == 0)
        {
            return null;
        }

        // Prioritize structure generated by the latest .NET version
        foreach (var frameworkVersion in cachedVersions.OrderByDescending(x => x))
        {
            var resources = await dataConnectionResourcesCache.GetResourcesAsync(dbConnection, frameworkVersion);
            if (resources.DatabaseStructure != null)
            {
                return resources.DatabaseStructure;
            }
        }

        return null;
    }
}
